package ac.github.os.internal.core.action.addon.dargoncore

import ac.github.os.api.OriginSkillAPI
import ac.github.os.api.common.DepPlugin
import ac.github.os.internal.core.NameKey
import ac.github.os.internal.core.action.ILocationAction
import ac.github.os.internal.core.config.StringConfig
import ac.github.os.internal.core.config.get
import ac.github.os.internal.session.Cache
import ac.github.os.internal.session.Session
import eos.moe.dragoncore.api.CoreAPI
import eos.moe.dragoncore.api.worldtexture.WorldTexture
import eos.moe.dragoncore.api.worldtexture.animation.Animation
import eos.moe.dragoncore.api.worldtexture.animation.RotateAnimation
import eos.moe.dragoncore.api.worldtexture.animation.ScaleAnimation
import eos.moe.dragoncore.api.worldtexture.animation.TranslateAnimation
import org.bukkit.Location
import org.bukkit.entity.Player
import taboolib.common.platform.Awake
import taboolib.common.platform.function.submit
import taboolib.common5.Coerce
import java.util.*


@DepPlugin("DragonCore")
@Awake
class WorldTextureAction : ILocationAction() {

    companion object {

        val list = mutableListOf<WorldTextureStrategy>()

        val lock = Any()

        @Awake
        fun runnable() {
            submit(async = true, period = 20) {
                synchronized(lock) {
                    val timeMillis = System.currentTimeMillis()
                    list.removeIf {
                        it.isEnd(timeMillis).apply { if (this) it.removeAll() }
                    }
                }
            }
        }

    }

    override fun onExecute(session: Session, cache: Cache, config: StringConfig) {

        val list = config.get<List<String>>("animations", listOf())?.map {
            val dataPair = OriginSkillAPI.check(it)
            val animation = IAnimation.impls.first { it.namespace == dataPair.key }
            dataPair.value.initial(session)
            animation.transform(dataPair.value)
        } ?: arrayListOf()

        val keep = config.get("keep", -1L)!!
        val currentTimeMillis = System.currentTimeMillis()
        cache.filterIsLocation().forEach {
            val worldTexture = config.createWorldTexture(it) ?: return
            worldTexture.animationList.addAll(list)
            val randomUUID = UUID.randomUUID()
            val players = arrayListOf<Player>()
            val strategy = WorldTextureStrategy(players, randomUUID, currentTimeMillis + keep * 1000)
            it.world?.getNearbyEntities(it, 50.0, 50.0, 50.0)?.filterIsInstance<Player>()?.forEach { player ->
                players.add(player)
                CoreAPI.setPlayerWorldTextureItem(player, randomUUID.toString(), worldTexture)
            }
            if (keep != -1L) {
                synchronized(lock) { WorldTextureAction.list.add(strategy) }
            }
        }
    }

    fun StringConfig.createWorldTexture(location: Location): WorldTexture? {
        val rotate = get("rotate", listOf("0", "0", "0"))?.map { Coerce.toFloat(it) }!!
        val path = get<String>("path") ?: return null
        val width = get("width", 2f)!!
        val height = get("height", 2f)!!
        val alpha = get("alpha", 1f)!!
        val follow = get("follow", false)!!
        val glow = get("glow", false)!!
        return WorldTexture().apply {
            rotateX = rotate[0]
            rotateY = rotate[1]
            rotateZ = rotate[2]
            translateX = location.x
            translateY = location.y
            translateZ = location.z
            world = location.world!!.name
            this.glow = glow
            this.followPlayerEyes = follow
            this.alpha = alpha
            this.width = width
            this.height = height
            this.path = path
        }
    }

    override val namespace: String
        get() = "dc:world:texture"
}

abstract class IAnimation<T : Animation> : NameKey {

    companion object {
        val impls = listOf(ITranslateAnimation(), IRotateAnimation(), IScaleAnimation())
    }

    abstract fun transform(config: StringConfig): T
}

class ITranslateAnimation : IAnimation<TranslateAnimation>() {
    override val namespace: String
        get() = "translate"

    override fun transform(config: StringConfig): TranslateAnimation {
        val animation = TranslateAnimation()
        config.get<String>("direction")?.let { animation.direction = it }
        config.get<Int>("duration")?.let { animation.duration = it }
        config.get<Int>("delay")?.let { animation.delay = it }
        config.get<Float>("distance")?.let { animation.distance = it }
        config.get<Int>("cycle")?.let { animation.cycleCount = it }
        config.get<Int>("resetTime")?.let { animation.resetTime = it }
        //播放结束 是否固定结束的位子
        config.get<Boolean>("fixed")?.let { animation.fixed = it }
        config.get<Boolean>("bindEntity")?.let { animation.bindEntity = it }
        return animation
    }

}

class IRotateAnimation : IAnimation<RotateAnimation>() {
    override val namespace: String
        get() = "rotate"

    override fun transform(config: StringConfig): RotateAnimation {
        val animation = RotateAnimation()
        //旋转的方向
        config.get<String>("direction")?.let { animation.direction = it }
        //旋转的角度
        config.get<Float>("angle")?.let { animation.angle = it }
        config.get<Int>("delay")?.let { animation.delay = it }
        //从多少毫秒旋转到 angle
        config.get<Int>("duration")?.let { animation.duration = it }
        //动画播放的次数 -1 一直播放
        config.get<Int>("cycleCount")?.let { animation.cycleCount = it }
        //是否 旋转到了 angle 就停止播放
        config.get<Boolean>("fixed")?.let { animation.fixed = it }
        //重置时间
        config.get<Int>("resetTime")?.let { animation.resetTime = it }
        return animation
    }

}

class IScaleAnimation : IAnimation<ScaleAnimation>() {
    override val namespace: String
        get() = "scale"

    override fun transform(config: StringConfig): ScaleAnimation {
        val animation = ScaleAnimation()
        config.get<Int>("delay")?.let { animation.delay = it }
        //开始的值
        config.get<Float>("fromScale")?.let { animation.fromScale = it }
        config.get<Float>("toScale")?.let { animation.toScale = it }
        config.get<Int>("duration")?.let { animation.duration = it }
        config.get<Int>("cycleCount")?.let { animation.cycleCount = it }
        config.get<Boolean>("fixed")?.let { animation.fixed = it }
        config.get<Int>("resetTime")?.let { animation.resetTime = it }
        return animation
    }

}